package mgodb

import (
	"fmt"
	"github.com/facebookgo/inject"
	"github.com/globalsign/mgo"
	"github.com/globalsign/mgo/bson"
	"github.com/pkg/errors"
	"jgt-oper/src/model"
	"jgt-oper/src/schema"
	"jgt-oper/src/service/mongo"
	"time"
)

// cEntranceEquipment 门禁设备和门牌的对应关系
const cEntranceEquipment = "oper_entrance"

// EntranceEquipment 门禁设备数据表
type EntranceEquipment struct {
	DB *mongo.DB
}

// Init 初始化
func (a *EntranceEquipment) Init(g *inject.Graph, db *mongo.DB) {
	a.DB = db
	// 初始化mongo索引
	a.DB.C(cEntranceEquipment, func(c *mgo.Collection) error {
		c.EnsureIndex(mgo.Index{
			Key:    []string{"record_id", "deleted"},
			Unique: true,
		})
		c.EnsureIndexKey("record_id")
		return nil
	})
	var val model.IEntranceEquipment = a
	g.Provide(&inject.Object{Value: val, Name: "IEntranceEquipment"})
}

// Create 创建设备信息
func (a *EntranceEquipment) Create(item *schema.EntranceEquipment) error {
	err := a.DB.C(cEntranceEquipment, func(c *mgo.Collection) error {
		id, err := a.DB.IncrID(cEntranceEquipment)
		if err != nil {
			return err
		}
		item.ID = id
		return c.Insert(item)
	})
	if err != nil {
		return errors.Wrapf(err, "创建数据发生错误")
	}
	return nil
}

// QueryPage 查询设备详情列表分页数据
func (a *EntranceEquipment) QueryPage(params schema.EntranceEquipment, pageIndex, pageSize uint) (int64, []*schema.EntranceEquipment, error) {
	query := bson.M{
		"deleted": 0,
	}
	if v := params.EquipmentID; v != "" {
		query["equipment_id"] = bson.M{"$regex": v}
	}
	if v := params.EquipmentName; v != "" {
		query["equipment_name"] = bson.M{"$regex": v}
	}
	if v := params.EquipmentType; v != "" {
		query["equipment_type"] = bson.M{"$regex": v}
	}
	if v := params.GuardZoneType; v != "" {
		query["guard_zone_type"] = bson.M{"$regex": v}
	}
	//查询分页数据
	var total int64
	var items []*schema.EntranceEquipment
	err := a.DB.C(cEntranceEquipment, func(c *mgo.Collection) error {
		n, err := c.Find(query).Count()
		if err != nil {
			return err
		}
		total = int64(n)
		if total == 0 {
			return nil
		}
		return c.Find(query).Sort("-_id").Skip(int((pageIndex - 1) * pageSize)).Limit(int(pageSize)).All(&items)
	})
	if err != nil {
		return 0, nil, errors.Wrapf(err, "查询分页数据发生错误")
	}
	//对每个条目数据的管控区域进行添加 管控区域 的节点全路径名称
	for _, v := range items {
		if len(v.GuardZoneNodes) > 0 {
			var temp []*schema.BuildingTreeNode
			f := FibonacciLoop(a, v.GuardZoneNodes[0])
			var nameString string
			//循环执行  斐波那契数列函数 返回的函数，利用函数返回值进行判断退出循环时机。
			for true {
				v, ok := f()
				if !ok {
					temp = v
					break
				}
			}
			//构造管控节点名字
			count := len(temp)
			for i := 1; (count - i) > 0; i++ {
				nameString = nameString + "/" + temp[count-i].Name
			}
			nameString += "/"
			//以上将管控的多个区域的（相同）根路径组装完成，下面对管控的多个区域的相对路径进行拼装；
			//例如，上面组装了/汉峪金谷/A区/1#楼/1单元/1层/，下面组装101，102，103，等类似的名称
			for _, node := range v.GuardZoneNodes {
				buildingNode, errQueryNode := a.GetBuildingTreeNode(node)
				if errQueryNode != nil {
					return 0, nil, errors.Wrapf(errQueryNode, "查询管控区域对应的建筑节点出错！")
				}
				nameString = nameString + buildingNode.Name + ","
			}
			v.GuardZoneName = nameString
		}
	}
	return total, items, nil
}

// FibonacciLoop 斐波那契数列函数，循环出给定建筑节点的所有直系父节点。获取所有直系父节点，大多数情况是为了构建该建筑节点的全路径名。
func FibonacciLoop(a *EntranceEquipment, paramString string) func() ([]*schema.BuildingTreeNode, bool) {
	//闭包数据结构ab parentNode
	ab := make([]*schema.BuildingTreeNode, 0)
	parentNode := paramString
	//返回函数
	f := func() ([]*schema.BuildingTreeNode, bool) {
		//分配内存，用于存储查询的结果
		abc := new(schema.BuildingTreeNode)
		//查询父节点
		err := a.DB.C(cPark, func(c *mgo.Collection) error {
			return c.Find(bson.M{"record_id": parentNode, "deleted": 0}).One(abc)
		})
		if err != nil {
			return nil, false
		}
		//将查询的父节点追加到闭包数组变量中
		if abc.ParentNode == "" {
			parentNode = ""
			ab = append(ab, abc)
			return ab, false
		}
		parentNode = abc.ParentNode
		ab = append(ab, abc)
		return ab, true
	}
	return f
}

// GetDetailCard 查询设备信息详情,param 为门禁设备的record_id
func (a *EntranceEquipment) GetDetailCard(param string) (*schema.EntranceEquipment, error) {
	result := new(schema.EntranceEquipment)
	err := a.DB.C(cEntranceEquipment, func(c *mgo.Collection) error {
		return c.Find(bson.M{"record_id": param, "deleted": 0}).One(result)
	})
	if err != nil {
		if err == mgo.ErrNotFound {
			return nil, nil
		}
		return nil, errors.Wrapf(err, "精确查询数据发生错误")
	}
	//给查询的结果中的管控区域节点进行名字组装
	if len(result.GuardZoneNodes) > 0 {
		var temp []*schema.BuildingTreeNode
		f := FibonacciLoop(a, result.GuardZoneNodes[0])
		var nameString string
		//循环执行  斐波那契数列函数 返回的函数，利用函数返回值进行判断退出循环时机。
		for true {
			v, ok := f()
			if !ok {
				temp = v
				break
			}
		}
		//构造管控节点名字
		count := len(temp)
		for i := 1; (count - i) > 0; i++ {
			nameString = nameString + "/" + temp[count-i].Name
		}
		nameString += "/"
		for _, node := range result.GuardZoneNodes {
			buildingNode, errQueryNode := a.GetBuildingTreeNode(node)
			if errQueryNode != nil {
				return nil, errors.Wrapf(errQueryNode, "查询管控区域对应的建筑节点出错！")
			}
			nameString = nameString + buildingNode.Name + ","
		}
		result.GuardZoneName = nameString
	}
	fmt.Println(*result)
	//鉴别是否含有 父设备节点
	return result, nil
}

// FullName 根据给定的建筑节点的record_id字符串查询返回该节点的全路径名称.nodeID为建筑节点的record_id,返回值string为全路径名称。
func FullName(a *EntranceEquipment, nodeID string) (string, error) {
	var temp []*schema.BuildingTreeNode
	f := FibonacciLoop(a, nodeID)
	var fullName string
	//循环执行  斐波那契数列函数 返回的函数，利用函数返回值进行判断退出循环时机。
	for true {
		v, ok := f()
		if !ok {
			temp = v
			break
		}
	}
	//构造管控节点名字
	count := len(temp)
	for i := 1; (count - i) > 0; i++ {
		fullName = fullName + "/" + temp[count-i].Name
	}
	result, err := a.GetBuildingTreeNode(nodeID)
	if err != nil {
		return "", err
	}
	fullName = fullName + "/" + result.Name
	return fullName, nil
}

// QueryTreeNode 查询返回建筑节点树,其实这里返回的是一个简单的建筑节点的slice，不需要构建树形结构。前台页面需要此数据的组件自动构建树结构。
func (a *EntranceEquipment) QueryTreeNode() ([]*schema.BuildingNode, error) {
	querytest := bson.M{
		"deleted": 0,
	}
	var testQuery []*schema.BuildingNode
	testErr := a.DB.C(cPark, func(c *mgo.Collection) error {
		return c.Find(querytest).All(&testQuery)
	})
	if testErr != nil {
		return nil, errors.Wrapf(testErr, "查询数据发生错误****1")
	}
	for _, value := range testQuery {
		value.Value = value.RecordID
	}
	/***********分片数组转换成树形结构********start******
	//给每个建筑节点赋值全名
	for _ , value := range testQuery {
		fullName , err := FullName(a,value.RecordID)
		if err != nil {
			return nil,err
		}
		value.FullName = fullName
	}
	//结构体指针的分片 转换成 map的分片
	var sliceMap = make([]map[string]interface{}, 0)
	for _, node := range testQuery {
		tempMap := util.StructToMap(*node)
		sliceMap = append(sliceMap, tempMap)
	}
	//map分片转换成树型结构
	treeMap := util.Slice2Tree(sliceMap, "value", "parent_id")
	return treeMap, nil
	**********分片数组转换成树形结构********end********/
	return testQuery, nil
}

// GetBuildingTreeNode 查询以 record_id为参数的的建筑节点信息
func (a *EntranceEquipment) GetBuildingTreeNode(param string) (*schema.BuildingTreeNode, error) {
	item := new(schema.BuildingTreeNode)
	if param == "" {
		return nil, errors.New("查询参数出错！！！")
	}
	err := a.DB.C(cPark, func(c *mgo.Collection) error {
		return c.Find(bson.M{"record_id": param, "deleted": 0}).One(item)
	})
	if err != nil {
		return nil, errors.Wrapf(err, "查询数据发生错误****")
	}
	return item, nil
}

// Delete 删除指定的门禁设备信息：仅在设备信息上delete字段上标注删除的时间戳
func (a *EntranceEquipment) Delete(param string) error {

	info := map[string]interface{}{
		"deleted": time.Now().Unix(),
	}
	err := a.DB.C(cEntranceEquipment, func(c *mgo.Collection) error {
		return c.Update(bson.M{"record_id": param}, bson.M{"$set": info})
	})
	if err != nil {
		return errors.Wrapf(err, "删除数据发生错误")
	}
	return nil
}

// GetEquipmentInfo 获取上级设备清单。param 为管控区域的建筑节点record_id
func (a *EntranceEquipment) GetEquipmentInfo(param string) ([]*schema.GetEquipmentInfo, error) {
	//先循环出所有直系上级建筑节点，从指定的建筑节点到跟节点的所有节点
	f := FibonacciLoop(a, param)
	var temp []*schema.BuildingTreeNode
	for true {
		v, ok := f()
		if !ok {
			temp = v
			break
		}
	}
	//从上面的叶节点到根节点依次查找绑定的门禁设备
	var items []*schema.GetEquipmentInfo
	for _, value := range temp {
		items = nil
		query := bson.M{"guard_zone": value.RecordID}
		err := a.DB.C(cEntranceEquipment, func(c *mgo.Collection) error {
			return c.Find(query).All(&items)
		})
		if err != nil {
			return nil, errors.Wrapf(err, "查询上级设备信息发生错误")
		}
		if len(items) > 0 {
			break
		}
	}
	return items, nil
}

// QueryEntranceArray 批量查询门禁设备
func (a *EntranceEquipment) QueryEntranceArray(entranceIDArray []string) ([]*schema.EntranceEquipment, error) {
	query := bson.M{
		"deleted":   0,
		"record_id": bson.M{"$in": entranceIDArray},
	}
	fmt.Println("entranceIDArray:", entranceIDArray)
	var items []*schema.EntranceEquipment
	err := a.DB.C(cEntranceEquipment, func(c *mgo.Collection) error {
		return c.Find(query).All(&items)
	})
	if err != nil {
		return nil, err
	}

	fmt.Println("items:", items)

	return items, nil
}
